package com.dhq.myapp.util.ask

import java.io.*

/**
 * Base64 编码和解码。
 *
 * @author jiangshuai
 * @date 2016年10月03日
 */
object Base64Util {

    // Base64 字母表
    private val alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".toCharArray()

    // Base64 字符到值的查找表
    private val codes = ByteArray(256)

    init {
        // 初始化查找表
        for (i in codes.indices) {
            codes[i] = -1
        }

        for (i in 'A'..'Z') {
            codes[i.code] = (i - 'A').toByte()
        }

        for (i in 'a'..'z') {
            codes[i.code] = (26 + (i - 'a')).toByte()
        }

        for (i in '0'..'9') {
            codes[i.code] = (52 + (i - '0')).toByte()
        }

        codes['+'.code] = 62
        codes['/'.code] = 63
    }

    /**
     * 功能：编码字符串
     *
     * @param data 源字符串
     * @return 编码后的字符串
     */
    fun encode(data: String): String {
        return String(encode(data.toByteArray()))
    }

    /**
     * 功能：解码字符串
     *
     * @param data 源字符串
     * @return 解码后的字符串
     */
    fun decode(data: String): String {
        return String(decode(data.toCharArray()))
    }

    /**
     * 功能：编码byte[]
     *
     * @param data 源字节数组
     * @return 编码后的字符数组
     */
    fun encode(data: ByteArray): CharArray {
        val out = CharArray(((data.size + 2) / 3) * 4)

        var index = 0
        for (i in data.indices step 3) {
            var quad = false
            var trip = false

            var value = (data[i].toInt() and 0xFF)
            value = value shl 8

            if (i + 1 < data.size) {
                value = value or (data[i + 1].toInt() and 0xFF)
                trip = true
            }

            value = value shl 8

            if (i + 2 < data.size) {
                value = value or (data[i + 2].toInt() and 0xFF)
                quad = true
            }

            out[index + 3] = alphabet[if (quad) value and 0x3F else 64]
            value = value shr 6
            out[index + 2] = alphabet[if (trip) value and 0x3F else 64]
            value = value shr 6
            out[index + 1] = alphabet[value and 0x3F]
            value = value shr 6
            out[index] = alphabet[value and 0x3F]

            index += 4
        }

        return out
    }

    /**
     * 功能：解码
     *
     * @param data 编码后的字符数组
     * @return 解码后的字节数组
     */
    fun decode(data: CharArray): ByteArray {
        var tempLen = data.size
        for (c in data) {
            if (c.code > 255 || codes[c.code] < 0) {
                tempLen-- // 忽略非有效字符和填充
            }
        }

        // 计算所需长度
        val len = (tempLen / 4) * 3 + when (tempLen % 4) {
            3 -> 2
            2 -> 1
            else -> 0
        }

        val out = ByteArray(len)
        var shift = 0 // 存储在 accum 中的多余位数
        var accum = 0 // 多余位
        var index = 0

        for (c in data) {
            val value = if (c.code > 255) -1 else codes[c.code].toInt()

            if (value >= 0) { // 跳过非代码字符
                accum = accum shl 6
                shift += 6
                accum = accum or value

                if (shift >= 8) {
                    shift -= 8
                    out[index++] = (accum shr shift and 0xFF).toByte()
                }
            }
        }

        if (index != out.size) {
            throw Error("Miscalculated data length (wrote $index instead of ${out.size})")
        }

        return out
    }

    /**
     * 功能：编码文件
     *
     * @param file 源文件
     */
    @Throws(IOException::class)
    fun encode(file: File) {
        if (!file.exists()) {
            return
        }

        val decoded = readBytes(file)
        val encoded = encode(decoded)
        writeChars(file, encoded)
    }

    /**
     * 功能：解码文件。
     *
     * @param file 源文件
     */
    @Throws(IOException::class)
    fun decode(file: File) {
        if (!file.exists()) {
            return
        }

        val encoded = readChars(file)
        val decoded = decode(encoded)
        writeBytes(file, decoded)
    }

    @Throws(IOException::class)
    private fun readBytes(file: File): ByteArray {
        ByteArrayOutputStream().use { baos ->
            FileInputStream(file).use { fis ->
                BufferedInputStream(fis).use { bis ->
                    val buf = ByteArray(16384)
                    var count: Int
                    while (bis.read(buf).also { count = it } != -1) {
                        if (count > 0) {
                            baos.write(buf, 0, count)
                        }
                    }
                }
            }
            return baos.toByteArray()
        }
    }

    @Throws(IOException::class)
    private fun readChars(file: File): CharArray {
        CharArrayWriter().use { caw ->
            FileReader(file).use { fr ->
                BufferedReader(fr).use { br ->
                    val buf = CharArray(16384)
                    var count: Int
                    while (br.read(buf).also { count = it } != -1) {
                        if (count > 0) {
                            caw.write(buf, 0, count)
                        }
                    }
                }
            }
            return caw.toCharArray()
        }
    }

    @Throws(IOException::class)
    private fun writeBytes(file: File, data: ByteArray) {
        FileOutputStream(file).use { fos ->
            BufferedOutputStream(fos).use { bos ->
                bos.write(data)
            }
        }
    }

    @Throws(IOException::class)
    private fun writeChars(file: File, data: CharArray) {
        FileWriter(file).use { fw ->
            BufferedWriter(fw).use { bw ->
                bw.write(data)
            }
        }
    }
}